package com.ruoyi.common.config.pay;


import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * 功能描述： AES工具类
 *
 * @author lingyt
 * 修改历历史：
 * 1.create on 2019/8/10 by lingyt for 2019PJDHEJ
 */
//===modify BY shencq ON 2020-02-20 FOR 33103| REVIEW BY xiaowq ON 2020-02-29   ===

public class AESUtils {

    /**
     * 业务数据加密方式:AES
     */
    public static final String ENCRYPT_ALGORITHM = "AES";
    /**
     * 安全随机签名算法
     */
    public static final String SECURE_ALGORITHM = "SHA1PRNG";

    /**
     * 随机key的加密算法：RSA
     */
    public static final String KEY_ENCRYPT_ALGORITHM = "RSA";
    /**
     * 加密算法
     */
    public static final String SECRYPT_ALGORITHM = "AES/ECB/PKCS5Padding";

    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    /**
     * 报文加密
     *
     * @param randomKey 随机密钥
     * @param plainData 上送报文
     * @return 报文密文
     * @throws Exception
     */
    public static String encrypt(String plainData, String randomKey) throws Exception {
        try {
            SecretKey secretKey = generateSecretKey(randomKey);
            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getEncoded(), ENCRYPT_ALGORITHM);
            Security.addProvider(new BouncyCastleProvider());
            Cipher aesCipher = Cipher.getInstance(SECRYPT_ALGORITHM, "BC");
            aesCipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] aesData = aesCipher.doFinal(plainData.getBytes(DEFAULT_CHARSET));
            return Base64.encodeBase64String(aesData);
        } catch (Exception e) {
            throw new Exception("加密失败");
        }
    }

    /**
     * AES加密
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static String encryptWithBC(String key, String data) throws Exception {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(32);
            buffer.put(key.getBytes());
            KeyParameter kp = new KeyParameter(buffer.array());
            byte[] bytes = data.getBytes(DEFAULT_CHARSET);

            CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
            cipher.init(true, kp);

            byte[] output = new byte[cipher.getOutputSize(bytes.length)];
            int len = cipher.processBytes(bytes, 0, bytes.length, output, 0);
            cipher.doFinal(output, len);

            return Base64.encodeBase64String(output);
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

    public static String decryptWithBC(String data, String key) throws Exception {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(32);
            buffer.put(key.getBytes());
            KeyParameter kp = new KeyParameter(buffer.array());

            byte[] bytes = Base64.decodeBase64(data);

            CBCBlockCipher aes = new CBCBlockCipher(new AESEngine());
            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(aes, new PKCS7Padding());
            cipher.init(false, kp);

            byte[] output = new byte[cipher.getOutputSize(bytes.length)];
            int len = cipher.processBytes(bytes, 0, bytes.length, output, 0);
            int len2 = cipher.doFinal(output, len);
            byte rawData[] = new byte[len + len2];
            System.arraycopy(output, 0, rawData, 0, rawData.length);
            String plainData = new String(rawData, DEFAULT_CHARSET);
            return plainData;
        } catch (Exception e) {
            throw new Exception("解密失败");
        }
    }

    /**
     * 报文解密
     *
     * @param aesBase64
     * @param randomKey
     * @return
     * @throws Exception
     */
    public static String decrypt(String aesBase64, String randomKey) throws Exception {
        try {
            byte[] aesData = Base64.decodeBase64(aesBase64);
            KeyGenerator aesGen = KeyGenerator.getInstance(ENCRYPT_ALGORITHM);
            SecureRandom secureRandom = SecureRandom.getInstance(SECURE_ALGORITHM);
            secureRandom.setSeed(randomKey.getBytes());
            aesGen.init(128, secureRandom);
            SecretKey secretKey = aesGen.generateKey();
            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getEncoded(), ENCRYPT_ALGORITHM);
            Security.addProvider(new BouncyCastleProvider());
            Cipher aesCipher = Cipher.getInstance(SECRYPT_ALGORITHM, "BC");
            aesCipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] plain = aesCipher.doFinal(aesData);
            String plainData = new String(plain, DEFAULT_CHARSET);
            return plainData;
        } catch (Exception e) {
            throw new Exception("解密失败");
        }
    }


    public static SecretKey generateSecretKey(String randomKey) throws Exception {
        return generateSecretKey(randomKey, SECURE_ALGORITHM);
    }

    public static SecretKey generateSecretKey(String randomKey, String secureAlgorithm) throws Exception {
        try {
            KeyGenerator aesGen = KeyGenerator.getInstance(ENCRYPT_ALGORITHM);
            SecureRandom secureRandom = SecureRandom.getInstance(secureAlgorithm);
            secureRandom.setSeed(randomKey.getBytes());
            aesGen.init(128, secureRandom);
            SecretKey secretKey = aesGen.generateKey();
            return secretKey;
        } catch (Exception e) {
            throw new Exception("解密失败");
        }
    }

    /**
     * 生成随机密钥，一次一密
     *
     * @return
     */
    public static String getRandomKey() {
        SecureRandom random = new SecureRandom();
        long randomKey = random.nextLong();
        return String.format("%012x", randomKey);
    }

    public static String generateAESKey() {
        KeyGenerator keyGenerator = null;
        try {
            keyGenerator = KeyGenerator.getInstance(ENCRYPT_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
        keyGenerator.init(128);
        SecretKey key = keyGenerator.generateKey();
        byte[] keyExternal = key.getEncoded();
        return Base64.encodeBase64String(keyExternal);
    }

    public static byte[] decryptByPrivateKey(byte[] encryptedData,
                                             String privateKey, String transformation) throws Exception {
        byte[] keyBytes = java.util.Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ENCRYPT_ALGORITHM);
        RSAPrivateKey privateK = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return cipher.doFinal(encryptedData);
    }

}

